package com.zl.quickdoc.framework.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.zl.quickdoc.model.ApiModule;
import com.zl.quickdoc.model.DocCfgParam;
import com.zl.quickdoc.model.UriAndMethod;
import com.zl.quickdoc.utils.ParserJavaUtils;

/**
 * 解析spirng web框架
 * @author zhouliang
 *
 */
public class SpringWebHttpFramework extends AbstractHttpFramework {
    public SpringWebHttpFramework() {
        super();
    }
    public SpringWebHttpFramework(DocCfgParam cfg) {
        super(cfg);
    }
    
    @Override
    public boolean support(List<CompilationUnit> cus) {
       return cus.stream().anyMatch(cu-> 
          cu.getType(0).asClassOrInterfaceDeclaration().getAnnotationByName("Controller").isPresent()
              || cu.getType(0).asClassOrInterfaceDeclaration().getAnnotationByName("RestController").isPresent());
    }
    
    /**
     * 获取接口的uri
     */
    @Override
    public UriAndMethod getUrisAndMethods(CompilationUnit cu, MethodDeclaration md) {
        UriAndMethod parentUrisAndMethod = getParentUri(cu);
        UriAndMethod uriAndMethod = getMethodUri(md);
        if (parentUrisAndMethod==null && uriAndMethod == null) {
           return null;
        }
       
        if (uriAndMethod==null) {
            return parentUrisAndMethod;
        }
        if (parentUrisAndMethod==null) {
            return uriAndMethod;
        }
        List<String> wholeUris = new ArrayList<>(1);
        for (String parentPath : parentUrisAndMethod.getUris()) {
            List<String> values = uriAndMethod.getUris();
            if(values!=null && !values.isEmpty()) {
                for (String value : values) {
                    String uri;
                    if (parentPath.endsWith("/") && value.startsWith("/")) {
                        uri = parentPath.substring(0, parentPath.length() - 1) + value;
                    } else if (parentPath.length() > 0 && !parentPath.endsWith("/") && !value.startsWith("/")) {
                        uri = parentPath + '/' + value;
                    } else {
                        uri = parentPath + value;
                    }
                    wholeUris.add(uri);
                }
            }else {
                wholeUris.add(parentPath);
            }
        }
        if(uriAndMethod.getConsumes()==null) {
            uriAndMethod.setConsumes(parentUrisAndMethod.getConsumes());
        }
        if(uriAndMethod.getProduces()==null) {
            uriAndMethod.setProduces(parentUrisAndMethod.getProduces());
        }
        uriAndMethod.setUris(wholeUris);
        return uriAndMethod;
    }

    /**
     * 判断是否都返回json
     */
    @Override
    public boolean checkReturnJson(CompilationUnit cu , MethodDeclaration md) {
        return cu.getType(0).asClassOrInterfaceDeclaration().getAnnotationByName("RestController").isPresent()
                || md.getAnnotationByName("ResponseBody").isPresent();
    }
    
    @Override
    public List<ApiModule> extend(List<ApiModule> apiModules) throws Exception{
        return super.extend(apiModules);
    }

    private UriAndMethod getParentUri(CompilationUnit cu) {
        Map<String, Object> anonMap = ParserJavaUtils.getValueAnnotation(cu, "RequestMapping");
        if(!anonMap.isEmpty()) {
            return handleMethodUriInfo(anonMap,"RequestMapping", null);
        }
        return null;
    }
    private UriAndMethod getMethodUri(MethodDeclaration md) {
        Map<String, Object> anonMap = ParserJavaUtils.getValueAnnotation(md, "RequestMapping");
        if(!anonMap.isEmpty()) {
            //org.springframework.web.bind.annotation.RequestMethod[] method
            return handleMethodUriInfo(anonMap,"DeleteMapping", "未明确");
        }
        anonMap = ParserJavaUtils.getValueAnnotation(md, "PostMapping");
        if(!anonMap.isEmpty()) {
            return handleMethodUriInfo(anonMap,"PostMapping","POST");
        }
        anonMap = ParserJavaUtils.getValueAnnotation(md, "GetMapping");
        if(!anonMap.isEmpty()) {
            return handleMethodUriInfo(anonMap,"GetMapping","GET");
        }
        anonMap = ParserJavaUtils.getValueAnnotation(md, "PutMapping");
        if(!anonMap.isEmpty()) {
            return handleMethodUriInfo(anonMap,"PutMapping","PUT");
        }
        anonMap = ParserJavaUtils.getValueAnnotation(md, "DeleteMapping");
        if(!anonMap.isEmpty()) {
            return handleMethodUriInfo(anonMap,"DeleteMapping","DELETE");
        }
        anonMap = ParserJavaUtils.getValueAnnotation(md, "PatchMapping");
        if(!anonMap.isEmpty()) {
            return handleMethodUriInfo(anonMap,"PatchMapping","PATCH");
        }
        return null;
    }
    @SuppressWarnings("unchecked")
    private UriAndMethod handleMethodUriInfo(Map<String, Object> anonMap,String anonname,String defMethod) {
        UriAndMethod uriAndMethod = new UriAndMethod(); 
        List<String> uris = new ArrayList<>();

        if(anonMap.get("value") instanceof String) {
            uris.add((String)anonMap.get("value"));
        }else if(anonMap.get("value") instanceof List) {
            uris.addAll((List<String>) anonMap.get("value"));
        }else if(anonMap.get(anonname) instanceof String && !anonname.equals(anonMap.get(anonname))) {
            uris.add((String)anonMap.get(anonname));
        }else if(anonMap.get(anonname) instanceof List) {
            uris.addAll((List<String>) anonMap.get("value"));
        }
        uriAndMethod.setConsumes(anonMap.get("consumes") instanceof String?Arrays.asList((String)anonMap.get("consumes"))
                :(List<String>) anonMap.get("consumes"));
        uriAndMethod.setProduces(anonMap.get("produces") instanceof String?Arrays.asList((String)anonMap.get("produces"))
                :(List<String>) anonMap.get("produces"));
        uriAndMethod.setUris(uris);
        uriAndMethod.setMethods(anonMap.get("method") instanceof String?Arrays.asList((String)anonMap.get("method"))
                :(List<String>) anonMap.get("method"));
        if(defMethod!=null && (uriAndMethod.getMethods()==null || uriAndMethod.getMethods().isEmpty())) {
            uriAndMethod.setMethods(Arrays.asList(defMethod));
        }
        return uriAndMethod;
    }
}
